#pragma once
#include "Err.hpp"
#include "Sock.hpp"
#include "Log.hpp"
#include "epoller.hpp"
#include <iostream>
#include <functional>
#include <cassert>

const uint16_t DEFAULT_PORT = 8888;

using func_t = std::function<std::string(std::string)>;

class EpollServer
{
    const static int N = 128;

public:
    EpollServer(func_t func, uint16_t port = DEFAULT_PORT)
        : _port(port)
        , _func(func)
    {
    }
    ~EpollServer()
    {
        _listen_sock.Close();
        _epoller.Close();
    }
    void Init()
    {
        logMessage(Debug, "Epoll服务器，启动！");
        _listen_sock.Socket();
        _listen_sock.Bind(_port);
        _listen_sock.Listen();

        // 获取一个epoll的句柄
        _epoller.Create();
    }
    void Start()
    {
        // 让epoll等待监听套接字的读事件
        bool ret = _epoller.AddEvent(_listen_sock.Fd(), EPOLLIN);
        assert(ret);
        (void)ret;

        int timeout = -1;
        while (true)
        {
            // 等待，获取就绪的IO事件
            int n = _epoller.Wait(_evs, N, timeout);
            switch (n)
            {
            case 0:
                logMessage(Debug, "Timeout");
                break;
            case -1:
                logMessage(Warning, "epoll_wait failed");
                break;
            default:
                logMessage(Debug, "有%d个就绪事件发生了", n);
                HandleEvent(n);
                break;
            }
        }
    }
    /// @brief 处理n个就绪事件
    /// @param n 就绪事件的个数
    void HandleEvent(int n)
    {
        for (int i = 0; i < n; ++i)
        {
            int fd = _evs[i].data.fd;
            uint32_t events = _evs[i].events;
            logMessage(Debug, "当前正在处理%d上的%s事件", fd, (events & EPOLLIN) ? "EPOLLIN" : "OTHER");
            if (events & EPOLLIN) // 读事件就绪
            {
                if (fd == _listen_sock.Fd()) // 处理监听套接字的读事件
                    Acceptor();
                else // 处理IO套接字的读事件
                    Recver(fd);
            }
            else if (events & EPOLLOUT) // 写事件就绪
            {
                //...
            }
            else
            {
                //...
            }
        }
    }
    /// @brief 处理监听套接字的读事件
    void Acceptor()
    {
        std::string clientip;
        uint16_t clientport;
        // 此时Accept不会阻塞，因为listensocket已经读就绪了
        int sock = _listen_sock.Accept(&clientip, &clientport);
        if (sock < 0)
            return;
        logMessage(Debug, "%s:%d 已经连上了服务器了", clientip.c_str(), clientport);

        // 让epoll等待IO套接字的读事件
        bool r = _epoller.AddEvent(sock, EPOLLIN);
        assert(r);
        (void)r;
    }
    /// @brief 处理IO套接字的读事件
    /// @param fd IO套接字
    void Recver(int fd)
    {
        char request[1024];
        // 此时recv不会阻塞，因为IO套接字已经读就绪了
        ssize_t s = recv(fd, request, sizeof(request) - 1, 0);
        if (s > 0)
        {
            request[s] = 0;
            std::string response = _func(request);

            // std::string response = "[echo]";
            // response += request;
            send(fd, response.c_str(), response.size(), 0);
        }
        else
        {
            if (s == 0)
                logMessage(Info, "client quit ...");
            else
                logMessage(Warning, "recv error, client quit ...");
            // 客户端已经退出或者recv读取失败，应该关闭fd以及删除epoll的相关fd
            // 让epoll不再关心该文件描述符的IO事件
            _epoller.DelEvent(fd);
            // 关闭该文件描述符
            close(fd);
        }
    }

private:
    uint16_t _port;
    Sock _listen_sock;
    Epoller _epoller;
    struct epoll_event _evs[N]; // 已经就绪的fd
    func_t _func;
};